package io.gitee.zhangbinhub.admin.tools

import cn.dev33.satoken.context.SaHolder
import cn.dev33.satoken.oauth2.SaOAuth2Manager
import cn.dev33.satoken.oauth2.data.model.AccessTokenModel
import cn.dev33.satoken.oauth2.data.model.ClientTokenModel
import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception
import cn.dev33.satoken.oauth2.template.SaOAuth2Util
import cn.dev33.satoken.stp.StpUtil
import io.gitee.zhangbinhub.acp.core.common.CommonTools
import io.gitee.zhangbinhub.admin.constant.AcpConstant
import io.gitee.zhangbinhub.admin.vo.TokenUserInfoVo
import org.bouncycastle.util.encoders.Base64
import org.noear.snack4.ONode

object TokenTools {
    @JvmStatic
    fun getAccessToken(): String? =
        SaOAuth2Manager.getDataResolver().readAccessToken(SaHolder.getRequest())

    @JvmStatic
    fun getClientToken(): String? =
        SaOAuth2Manager.getDataResolver().readClientToken(SaHolder.getRequest())

    @JvmStatic
    @JvmOverloads
    fun getAccessTokenModel(accessToken: String? = null): AccessTokenModel? =
        SaOAuth2Util.getAccessToken(accessToken ?: getAccessToken())

    @JvmStatic
    @JvmOverloads
    fun getClientTokenModel(clientToken: String? = null): ClientTokenModel? =
        SaOAuth2Util.getClientToken(clientToken ?: getClientToken())

    @JvmStatic
    fun revokeAccessToken() {
        getAccessTokenModel()?.apply {
            revokeAccessToken(clientId, loginId)
        }
    }

    @JvmStatic
    fun revokeAccessToken(clientId: String?, loginId: Any?) {
        SaOAuth2Util.revokeAccessTokenByIndex(clientId, loginId)
        StpUtil.logout(loginId, clientId)
    }

    @JvmStatic
    fun revokeClientToken() {
        getClientTokenModel()?.apply {
            revokeClientToken(clientId)
        }
    }

    @JvmStatic
    fun revokeClientToken(clientId: String?) {
        SaOAuth2Util.revokeClientTokenByIndex(clientId)
    }

    @JvmStatic
    @Throws(SaOAuth2Exception::class)
    fun encryptUserInfo(userInfoVo: TokenUserInfoVo): String = try {
        Base64.toBase64String(ONode.serialize(userInfoVo).toByteArray(CommonTools.getDefaultCharset()))
    } catch (e: Exception) {
        throw SaOAuth2Exception(e.message)
    }

    @JvmStatic
    @Throws(SaOAuth2Exception::class)
    fun decryptUserInfo(ciphertext: String): TokenUserInfoVo = try {
        ONode.deserialize(
            String(Base64.decode(ciphertext), CommonTools.getDefaultCharset()),
            TokenUserInfoVo::class.java
        )
    } catch (e: Exception) {
        throw SaOAuth2Exception(e.message)
    }

    @JvmStatic
    @Throws(SaOAuth2Exception::class)
    fun getUserInfoFromToken(accessToken: String? = null): TokenUserInfoVo =
        getAccessTokenModel(accessToken)?.let { atm ->
            decryptUserInfo(atm.extraData[AcpConstant.tokenUserinfo].toString())
        } ?: TokenUserInfoVo()
}